﻿using GameFramework;
using GameFramework.Event;
using GameFramework.Network;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;
using Microsoft.IO;
using UnityGameFramework.Runtime;

namespace NKG.NetWork
{
   public class ET_NetworkChannelHelper : INetworkChannelHelper
    {
        private readonly Dictionary<int, Type> m_ServerToClientPacketTypes = new Dictionary<int, Type>();

        private readonly List<byte[]> byteses = new List<byte[]>()
            {new byte[ETPackets.ET_PacketSizeLength],  new byte[2]};

        public RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();

        private INetworkChannel m_NetworkChannel = null;

        private MemoryStream memoryStream = new MemoryStream(1024 * 8);

        /// <summary>
        /// 前2个字节代表消息的长度
        /// 第3和第4个字节表示消息的Id
        /// 这样做是为了和ET服务器的消息包的解析保持一致
        /// </summary>
        public int PacketHeaderLength
        {
            get { return ETPackets.ET_PacketSizeLength + 2; }
        }


        public void Initialize(INetworkChannel networkChannel)
        {
            m_NetworkChannel = networkChannel;

            // 反射注册包和包处理函数。
            Type packetBaseType = typeof(SCPacketBase);
            Type packetHandlerBaseType = typeof(PacketHandlerBase);
            Assembly assembly = Assembly.GetExecutingAssembly();
            Type[] types = assembly.GetTypes();
            for (int i = 0; i < types.Length; i++)
            {
                if (!types[i].IsClass || types[i].IsAbstract)
                {
                    continue;
                }

                if (types[i].BaseType == packetBaseType)
                {
                    PacketBase packetBase = (PacketBase) Activator.CreateInstance(types[i]);
                    Type packetType = GetServerToClientPacketType(packetBase.Id);
                    if (packetType != null)
                    {
                        Log.Warning("Already exist packet type '{0}', check '{1}' or '{2}'?.", packetBase.Id.ToString(),
                            packetType.Name, packetBase.GetType().Name);
                        continue;
                    }

                    m_ServerToClientPacketTypes.Add(packetBase.Id, types[i]);
                }
                else if (types[i].BaseType == packetHandlerBaseType)
                {
                    IPacketHandler packetHandler = (IPacketHandler) Activator.CreateInstance(types[i]);
                    m_NetworkChannel.RegisterHandler(packetHandler);
                }
            }

            Trinity.GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkConnectedEventArgs.EventId, OnNetworkConnected);
            Trinity.GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkClosedEventArgs.EventId, OnNetworkClosed);
            Trinity.GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs.EventId,
                OnNetworkMissHeartBeat);
            Trinity.GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkErrorEventArgs.EventId, OnNetworkError);
            Trinity.GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkCustomErrorEventArgs.EventId,
                OnNetworkCustomError);
        }

        public Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData)
        {
            customErrorData = null;
            ET_SCPacketHeader header = packetHeader as ET_SCPacketHeader;
            if (header == null)
            {
                Log.Warning("Packet header is invalid.");
                return null;
            }

            Packet packet = null;
            if (header.IsValid)
            {
                Type packetType = GetServerToClientPacketType(header.Id);
                if (packetType != null && source is MemoryStream)
                {
                    object instance = Activator.CreateInstance(packetType);
                    packet = (Packet) ProtobufHelper.FromStream(instance, (MemoryStream) source);
                }
                else
                {
                    Log.Warning("Can not deserialize packet for packet id '{0}'.", header.Id.ToString());
                }
            }
            else
            {
                Log.Warning("Packet header is invalid.");
            }

            ReferencePool.Release(header);
            return packet;
        }

        public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData)
        {
            customErrorData = null;

            ET_SCPacketHeader scHeader = ReferencePool.Acquire<ET_SCPacketHeader>();
            MemoryStream memoryStream = source as MemoryStream;
            if (memoryStream != null)
            {
                int packetSize = BitConverter.ToUInt16(memoryStream.GetBuffer(), 0);
                ushort opcode = BitConverter.ToUInt16(memoryStream.GetBuffer(), ETPackets.ET_MessageOpcodeIndex);

                //这里需要用服务端发过来的packetSize的值减去消息包中opcode的长度，
                //因为服务端在发送消息时设置的packetSize的值是包含opcode的，而
                //客户端在解析包头的时候已经解析了opcode，因此剩余要解析的数据长度要减去2（opcode的总长度是2个字节）
                scHeader.PacketLength = packetSize - ETPackets.ET_MessageIdentifyLength;
                scHeader.Id = opcode;
                return scHeader;
            }

            return null;
        }


        public bool SendHeartBeat()
        {
            return true;
        }

        public bool Serialize<T>(T packet, Stream destination) where T : Packet
        {
            PacketBase packetImpl = packet as PacketBase;
            if (packetImpl == null)
            {
                Log.Warning("Packet is invalid.");
                return false;
            }

            if (packetImpl.PacketType != PacketType.ClientToServer)
            {
                Log.Warning("Send packet invalid.");
                return false;
            }

            memoryStream.Seek(PacketHeaderLength, SeekOrigin.Begin);
            memoryStream.SetLength(PacketHeaderLength);
            ProtobufHelper.ToStream(packet, memoryStream);

            // 头部消息
            ET_CSPacketHeader packetHeader = ReferencePool.Acquire<ET_CSPacketHeader>();
            packetHeader.PacketLength =
                (int) memoryStream.Length - ETPackets.ET_PacketSizeLength; // 消息内容长度需要减去头部消息长度,只包含packetSize一个字段
            packetHeader.Id = (ushort) packet.Id;

            memoryStream.Position = 0;
            this.byteses[0].WriteTo(0, (ushort) packetHeader.PacketLength);
            this.byteses[1].WriteTo(0, packetHeader.Id);
            int index = 0;
            foreach (var bytes in this.byteses)
            {
                Array.Copy(bytes, 0, memoryStream.GetBuffer(), index, bytes.Length);
                index += bytes.Length;
            }

            ReferencePool.Release(packetHeader);

            memoryStream.WriteTo(destination);

            long len = destination.Length;
            long pos = destination.Position;
            byte[] temp = (destination as MemoryStream).GetBuffer();
            return true;
        }

        public void Shutdown()
        {
            Trinity.GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkConnectedEventArgs.EventId,
                OnNetworkConnected);
            Trinity.GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkClosedEventArgs.EventId, OnNetworkClosed);
            Trinity.GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs.EventId,
                OnNetworkMissHeartBeat);
            Trinity.GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkErrorEventArgs.EventId, OnNetworkError);
            Trinity.GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkCustomErrorEventArgs.EventId,
                OnNetworkCustomError);

            m_NetworkChannel = null;
            memoryStream.Dispose();
        }


        private Type GetServerToClientPacketType(int id)
        {
            Type type = null;
            if (m_ServerToClientPacketTypes.TryGetValue(id, out type))
            {
                return type;
            }

            return null;
        }


        private void OnNetworkConnected(object sender, GameEventArgs e)
        {
            UnityGameFramework.Runtime.NetworkConnectedEventArgs ne =
                (UnityGameFramework.Runtime.NetworkConnectedEventArgs) e;
            if (ne.NetworkChannel != m_NetworkChannel)
            {
                return;
            }

            Log.Info("Network channel '{0}' connected, local address '{1}:{2}', remote address '{3}:{4}'.",
                ne.NetworkChannel.Name, ne.NetworkChannel.LocalIPAddress, ne.NetworkChannel.LocalPort.ToString(),
                ne.NetworkChannel.RemoteIPAddress, ne.NetworkChannel.RemotePort.ToString());
        }

        private void OnNetworkClosed(object sender, GameEventArgs e)
        {
            UnityGameFramework.Runtime.NetworkClosedEventArgs
                ne = (UnityGameFramework.Runtime.NetworkClosedEventArgs) e;
            if (ne.NetworkChannel != m_NetworkChannel)
            {
                return;
            }

            Log.Info("Network channel '{0}' closed.", ne.NetworkChannel.Name);
        }


        private void OnNetworkMissHeartBeat(object sender, GameEventArgs e)
        {
            UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs ne =
                (UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs) e;
            if (ne.NetworkChannel != m_NetworkChannel)
            {
                return;
            }

            Log.Info("Network channel '{0}' miss heart beat '{1}' times.", ne.NetworkChannel.Name,
                ne.MissCount.ToString());

            if (ne.MissCount < 2)
            {
                return;
            }

            ne.NetworkChannel.Close();
        }

        private void OnNetworkError(object sender, GameEventArgs e)
        {
            UnityGameFramework.Runtime.NetworkErrorEventArgs ne = (UnityGameFramework.Runtime.NetworkErrorEventArgs) e;
            if (ne.NetworkChannel != m_NetworkChannel)
            {
                return;
            }

            Log.Info("Network channel '{0}' error, error code is '{1}', error message is '{2}'.",
                ne.NetworkChannel.Name, ne.ErrorCode.ToString(), ne.ErrorMessage);

            ne.NetworkChannel.Close();
        }

        private void OnNetworkCustomError(object sender, GameEventArgs e)
        {
            UnityGameFramework.Runtime.NetworkCustomErrorEventArgs ne =
                (UnityGameFramework.Runtime.NetworkCustomErrorEventArgs) e;
            if (ne.NetworkChannel != m_NetworkChannel)
            {
                return;
            }
        }
    }
}